/** @file         led_module.c
 *  @brief        驱动。
 *  @details      led 模块文件。
 *  @author       lzm
 *  @date         2021-03-06 10:23:03
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日志:
 **********************************************************
*/

/* 系统库 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

/* 私人库 */
#include "led_resource.h"
#include "led_drv.h"


/* 变量 */
dev_t led_dev_num_start; // 开始设备号
static struct cdev led_cdev[LED_DEV_CNT+1]; // 全设备+LED_DEV_CNT个子设备
static class *led_dev_class; // 设备类 (*用于设备节点*)

/* [drv][file_operations] */
static struct file_operations led_dev_fops = 
{
    .owner = THIS_MODULE,
    .open = led_dev_open,
    .release = led_dev_release,
    .write = led_dev_write,
    .read = led_dev_read,
}


/* [module][1] */
/** @brief   led_chrdev_init
  * @details led module 入口函数
  * @param  
  * @retval 
  * @author lzm
  */
static __init int led_chrdev_init(void)
{
    unsigned char i;

    printk("chrdev_init\n");

    /* [module][1][1] 申请设备号 */
    alloc_chrdev_region(&led_dev_num_start, 0, LED_DEV_CNT+1, LED_DEV_NAME);

    /* [module][1][2] 创建设备节点 */
    led_dev_class = class_create(THIS_MODULE, LED_DEV_CLASS);    

    for(i=0; i<LED_DEV_CLASS+1; i++)
    {
        /* [module][1][3] 初始化内核设备文件 */
        cdev_init(&led_cdev[i], &led_dev_fops); // 把驱动程序初始化到内核设备文件中

        /* [module][1][4] 把内核设备文件注册到内核 */
        cdev_add(&led_cdev[i], MKDEV(MAJOR(led_dev_num_start), MINOR(led_dev_num_start)+i), 1); // 内核设备文件绑定设备号，并注册到内核

        /* [module][1][5] 创建设备节点 */
        if(!i)
            device_creat(led_dev_class, NULL, MKDEV(MAJOR(led_dev_num_start), MINOR(led_dev_num_start)+i), NULL, LED_DEV_NAME); // 总设备
        else
            device_creat(led_dev_class, NULL, MKDEV(MAJOR(led_dev_num_start), MINOR(led_dev_num_start)+i), NULL, LED_DEV_NAME"_%d",i);
    }
    return 0;
}
module_init(led_chrdev_init);

/* [module][2] */
/** @brief   led_chrdev_exit
  * @details led module 出口函数
  * @param  
  * @retval 
  * @author lzm
  */
static __init int led_chrdev_exit(void)
{
    unsigned char i;

    printk("chrdev_exit!\n");

    /* my 设备文件初始化 */
    led_chrdev_init();

    for(i=0; i<LED_DEV_CNT+1; i++)
    {
        /* [module][2][1] 删除设备节点 */
        device_destory(led_dev_class, MKDEV(MAJOR(led_dev_num_start), MINOR(led_dev_num_start)+i));

        /* [module][2][2] 注销设备文件 */
        cdev_del(&led_cdev[i]);
    }

    /* [module][2][3] 归还设备号 */
    unregister_chrdev_region(led_dev_num_start, LED_DEV_CNT+1);

    /* [module][2][4] 删除设备类 */
    class_destory(led_dev_class);

    return 0;
}
module_exit(led_chrdev_exit);

/* [module][3] 协议 */
MODULE_AUTHOR("lizhuming");
MODULE_LICENSE("GPL");

